/*
 *  Copyright 2019-2025 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package cn.com.cdb.ldms.modules.security.config;

import cn.com.cdb.ldms.modules.security.security.JwtAccessDeniedHandler;
import cn.com.cdb.ldms.modules.security.security.JwtAuthenticationEntryPoint;
import cn.com.cdb.ldms.modules.security.security.TokenConfigurer;
import cn.com.cdb.ldms.modules.security.security.TokenProvider;
import cn.com.cdb.ldms.modules.security.service.OnlineUserService;
import cn.com.cdb.ldms.utils.AnonTagUtils;
import cn.com.cdb.ldms.utils.enums.RequestMethodEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.web.filter.CorsFilter;

import java.util.Map;
import java.util.Set;

/**
 * @author jizi
 */
@Configuration
@RequiredArgsConstructor
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SpringSecurityConfig {

    private final TokenProvider tokenProvider;
    private final CorsFilter corsFilter;
    private final JwtAuthenticationEntryPoint authenticationErrorHandler;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final ApplicationContext applicationContext;
    private final SecurityProperties properties;
    private final OnlineUserService onlineUserService;

    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
        // 去除 ROLE_ 前缀
        return new GrantedAuthorityDefaults("");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 密码加密方式
        return new BCryptPasswordEncoder();
    }

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 获取匿名标记
        Map<String, Set<String>> anonymousUrls = AnonTagUtils.getAnonymousUrl(applicationContext);

        // 配置HTTP安全
        http
                // 禁用 CSRF
                .csrf(csrf -> csrf.disable())
                // 添加 CORS 过滤器
                .addFilterBefore(corsFilter, AuthorizationFilter.class)
                // 异常处理
                .exceptionHandling(handling -> handling
                        .authenticationEntryPoint(authenticationErrorHandler)
                        .accessDeniedHandler(jwtAccessDeniedHandler)
                )
                // 防止iframe造成跨域
                .headers(headers -> headers
                        .frameOptions(frame -> frame.disable())
                )
                // 不创建会话
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .authorizeHttpRequests(auth -> auth
                                // 静态资源
//                                .requestMatchers(
//                                        HttpMethod.GET,
//                                        "/static/**"
////                                "/**/*.html",
////                                "/**/*.css",
////                                "/**/*.js",
////                                "/webSocket/**"
//                                ).permitAll()

                                // 开放API文档相关路径
                                .requestMatchers(
                                        "/swagger-ui.html",
                                        "/doc.html",
                                        "/swagger-resources/**",
                                        "/webjars/**",
                                        "/v3/api-docs/**",
                                        "/auth/login",
                                        "/auth/logout",
                                        "/auth/code",
                                        "/xxl-job-admin/**",
                                        "/static/**",
                                        "/view/**"
                                ).permitAll()
                                // 文件访问
                                .requestMatchers("/avatar/**").permitAll()
                                .requestMatchers("/file/**").permitAll()

                                // 监控端点
                                .requestMatchers("/druid/**").permitAll()

                                // 放行OPTIONS请求
                                .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()

                                // 自定义匿名访问URL
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
                                .requestMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()

                                // 所有请求都需要认证
                                .anyRequest().authenticated()
                )
                // 应用自定义Token配置
                .with(new TokenConfigurer(tokenProvider, properties, onlineUserService), customizer -> {
                });

        return http.build();
    }
}